#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <dirent.h>
#include "GameManager.h"
#include "GL/glut.h"
#include "include.h"

#include "ControlledObject.h"
#include "CpuControlledObject.h"
#include "CpuControlledVertibrateObject.h"
#include "PinnedObject.h"
#include "TranslatingObject.h"
#include "AbrahamLincoln.h"
#include "Background.h"
#include "ParallaxLayer.h"
#include "Material.h"
#include "MenuElement.h"
#include "MenuElementList.h"
#include "MenuSelectionElement.h"
#include "SupplyBarElement.h"
#include "TextBox.h"
#include "Menu.h"
#include "DialogueManager.h"
#include "Dictionary.h"
#include "PressureButtonObject.h"

#define TIME_BETWEEN_POSITIONS 0.005
void GameManager::SetUserInput(int key, bool isDepressed)
{
	switch(key)
	{
		case GLUT_KEY_LEFT: m_leftArrowDepressed = isDepressed; break;
		case GLUT_KEY_RIGHT: m_rightArrowDepressed = isDepressed; break;
		case GLUT_KEY_UP: m_upArrowDepressed = isDepressed; break;
		case GLUT_KEY_DOWN: m_downArrowDepressed = isDepressed; break;
	}
}
void GameManager::SetUserInput(unsigned char key, bool isDepressed)
{

	switch(key)
	{
		case 'W':
		case 'w': m_upKeyDepressed = isDepressed; break;
		case 'S':
		case 's': m_downKeyDepressed = isDepressed; break;
		case 'A':
		case 'a': m_leftKeyDepressed = isDepressed; break;
		case 'D':
		case 'd': m_rightKeyDepressed = isDepressed; break;
		case 'E':
		case 'e': m_actionKeyDepressed = isDepressed; break;
		case 'G':
		case 'g': m_grenadeKeyDepressed = isDepressed; break;
		case 'P':
		case 'p': m_pause = !m_pause;break;
		case ' ': m_jumpKeyDepressed = isDepressed; break;
		case (char)27: m_escKeyDepressed = isDepressed; break;
	}
}
void GameManager::SetUserInput(double x, double y)
{
	m_mouseLocation = Vector2D(x,y);
}
void GameManager::SetUserInput(int button, bool isDepressed, int x, int y)
{
	switch(button)
	{
		case GLUT_RIGHT_BUTTON: m_rightMouseDepressed = isDepressed; break;
		case GLUT_LEFT_BUTTON: m_leftMouseDepressed = isDepressed; break;
		case GLUT_WHEEL_UP: camera->ZoomIn(); break;
		case GLUT_WHEEL_DOWN: camera->ZoomOut(); break;
	}
	m_mouseLocation = Vector2D(x, y);
}
Vector2D GameManager::GetCameraSize()
{
	return camera->size;
}
void GameManager::Cancel()
{
	
	
	printf("Freeing features\n");
	if(featureList)
		delete(featureList);
	featureList = NULL;
	
	printf("Freeing border\n");
	if(testBorder)
		delete(testBorder);
	testBorder = NULL;
	
	printf("Freeing strings\n");
	if(stringList)
		delete(stringList);
	stringList = NULL;
	
	printf("Freeing objects\n");
	if(objectList)
		delete(objectList);
	objectList = NULL;
	
	//player = NULL;
	//camera->SetFocusObject(NULL,false);
	//camera->SetBackground(NULL);
	//camera->SetPosition(Vector2D(0,0));
	//camera->ResetSize();
	//Init("./Levels");
}
bool GameManager::IsPlaySuspended()
{
	return isInMenu || isInConversation;
}
void GameManager::HandleUserInput()
{
	//printf("handle user input\n");
	
	if(m_escKeyDepressed)
	{
		ShowMainMenu();
	}
	if(!IsPlaySuspended())
	{
		if(m_leftKeyDepressed)
		{
			if(player)
				player->MoveLeft();
		}
		if(m_rightKeyDepressed)
		{
			if(player)
				player->MoveRight();
		}
		if(m_actionKeyDepressed && !m_actionKeyWasDepressed)
		{
			printf("PRESSING action key\n");
			//try and talk to NPC's
			if(player)
			{
				if(objectList)
				{
					char * conversation = NULL;
					Object * object = objectList->Action(player->position, gameState, &conversation);
					if(object)
					{
						//something activated
						if(conversation)
						{
							//this object talked to me. We need to display a text box.
							ShowConversationBox(object, conversation);
						}	
					}
				}
			}	
		}
		if(m_grenadeKeyDepressed && !m_grenadeKeyWasDepressed)
		{
			if(player)
			{
				GrenadeObject * grenade = player->UseGrenade();
				if(grenade)
				{
					objectList->Add(grenade);	
				}
			}
		}
		if(m_jumpKeyDepressed && !m_jumpKeyWasDepressed)
		{
			if(player)
				player->Jump();
		}
		m_jumpKeyWasDepressed = m_jumpKeyDepressed;
		m_grenadeKeyWasDepressed = m_grenadeKeyDepressed;
		m_actionKeyWasDepressed = m_actionKeyDepressed;
		if(player)
				player->ControlBeard(m_leftArrowDepressed, m_rightArrowDepressed,
							 m_upArrowDepressed, m_downArrowDepressed);
	}
	if(m_leftMouseDepressed && !m_leftMouseWasDepressed)
	{
		if(isInLevel && !IsPlaySuspended())
		{
			if(player)
			{
				Vector2D adjustedLocation = m_mouseLocation + camera->position + Vector2D(-camera->size.x + 1.0 * (camera->size.x/50.0), -camera->size.y - 1.0 * (camera->size.y/50.0));
				player->ControlBeard(adjustedLocation);
			}
		}
		else
		{
			if(isInMenu)
				menuManager.Click(m_mouseLocation);
			else if(isInConversation)
				guiManager.Click(m_mouseLocation);
		}
	}
	else if(m_leftMouseDepressed)
	{
		if(player && !IsPlaySuspended())
		{
			Vector2D adjustedLocation = m_mouseLocation + camera->position + Vector2D(-camera->size.x + 1.0 * (camera->size.x/50.0), -camera->size.y - 1.0 * (camera->size.y/50.0));
			player->MoveBeard(adjustedLocation);
		}
	}
	else if( m_leftMouseWasDepressed)
	{
		if(player && !IsPlaySuspended())
			player->BeardRelease();	
	}
	m_leftMouseWasDepressed = m_leftMouseDepressed;
	
	m_rightMouseWasDepressed = m_rightMouseDepressed;
}
GameManager::GameManager()
{
	//printf("GameManager::Start\n");
	m_escKeyDepressed = false;
	m_upKeyDepressed = false;
	m_downKeyDepressed = false;
	m_leftKeyDepressed = false;
	m_rightKeyDepressed = false;
	m_jumpKeyDepressed = false;
	m_jumpKeyWasDepressed = false;
	m_leftArrowDepressed = false;
	m_rightArrowDepressed = false;
	m_upArrowDepressed = false;
	m_downArrowDepressed = false;
	m_rightMouseDepressed = false;
	m_rightMouseWasDepressed = false;
	m_leftMouseDepressed = false;
	m_leftMouseWasDepressed = false;
	m_grenadeKeyDepressed = false;
	m_grenadeKeyWasDepressed = false;
	m_actionKeyDepressed = false;
	m_actionKeyWasDepressed = false;
	m_pause = false;
	exit = false;
	bool * tValue = (bool *)malloc(sizeof(bool));
	*tValue = true;
	bool * fValue = (bool *)malloc(sizeof(bool));
	*fValue = false;
	gameState = new Dictionary( "true", (void*)tValue, BOOL_TYPE_STRING);
	gameState->Add( "false", (void*)fValue, BOOL_TYPE_STRING);
	
	/*Vector2D * vertices = (Vector2D *)malloc(sizeof(Vector2D)*4);
	vertices[0] = Vector2D(-3,3);
	vertices[1] = Vector2D(3,3);
	vertices[2] = Vector2D(3,-3);
	vertices[3] = Vector2D(-3,-3);
	
	CpuControlledVertibrateObject * object = new CpuControlledVertibrateObject(&(levelManager.textureManager),
					 			  Vector2D(10,-30),
					  			  vertices,
					  			  4,
					  			  1.0,
				      			  true,
				      			  "./Skeletons/dinoSkeleton.skl");
					
	objectList = new ObjectList(object);
	*/
	printManager.SetTextureManager(&(levelManager.textureManager));
	
	CreateGui();
	guiManager.Hide();
	
	CreateLevelMenu();
	menuManager.Hide(LEVEL_MENU_INDEX);
	CreateMainMenu();
	isInMenu = true;
	isInConversation = false;
	
	camera = new Camera();	
	camera->textureManager = &(levelManager.textureManager);	
	printf("GameManager::Camera created\n");
	Init("./Levels");
	//printManager.Print("This is a test", Vector2D(0,0.1), 0.02);
	lastThoughtTimestamp = (float)clock()/CLOCKS_PER_SEC;
	time(&lastTime);
//printf("GameManager::End\n");

}
void GameManager::ShowConversationBox(Object * object, char * conversation)
{
	printf("ShowConversationBox\n");
	if(conversationBox)
	{
		ObjectList list = ObjectList(false);
		list.Add(player);
		list.Add(object);
		camera->SetFocusObjectList(&list, false);
		isInConversation = true;
		conversationBox->SetText(object, camera, conversation);
		guiManager.Show(GUI_CBOX_INDEX);	
	}
}
void GameManager::CreateConversationBox()
{
	conversationBox = new ConversationBox(&(levelManager.textureManager), Vector2D(0,0), Vector2D(40,30), 2.0,this, &GameManager::TextBoxClicked);
	MenuElementList * cBoxList = new MenuElementList(conversationBox);
	Menu * cMenu = new Menu(cBoxList, Vector2D(30,15), GUI_CBOX_INDEX);
	guiManager.Add(cMenu);
	guiManager.Hide(GUI_CBOX_INDEX);
}
void GameManager::CreateGui()
{
	Vector2D * textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0,1);
	textureVertices[1] = Vector2D(10,1);
	textureVertices[2] = Vector2D(10,0);
	textureVertices[3] = Vector2D(0,0);
	MenuElement * guiBar = new MenuElement(&(levelManager.textureManager), GUI_BAR_TEXTURE, Vector2D(0,0), Vector2D(100,5), textureVertices);
	
	textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0.5,0.25);
	textureVertices[1] = Vector2D(1,0.25);
	textureVertices[2] = Vector2D(1,0);
	textureVertices[3] = Vector2D(0.5,0);
	StatusBarElement * healthBar = new StatusBarElement(&(levelManager.textureManager), BUTTON_TEXTURE, Vector2D(0,0), Vector2D(10,5), textureVertices, 100.0);
	this->healthBar = healthBar;
	
	textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(-0.5,0.5);
	textureVertices[1] = Vector2D(0.5,0.5);
	textureVertices[2] = Vector2D(0.5,-0.5);
	textureVertices[3] = Vector2D(-0.5,-0.5);
	SupplyBarElement * grenadeBar = new SupplyBarElement(&(levelManager.textureManager), GRENADE_TEXTURE, Vector2D(15,0.5), Vector2D(12,4), textureVertices, &(supplyBag.grenades));
	
	/*textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0,1);
	textureVertices[1] = Vector2D(1,1);
	textureVertices[2] = Vector2D(1,0);
	textureVertices[3] = Vector2D(0,0);
	TextBox * textBox = new TextBox("This is a test of the text box, it is supposed to be able to wrap text for me. I also hope it can handle other things.",
								 &(levelManager.textureManager), "./Images/testPortrait.bmp", "./Images/testBorder.bmp", 
				 				Vector2D(30,15), Vector2D(40,30), 
				 				textureVertices, NULL,
								2.0, this, &GameManager::TextBoxClicked);*/
				 
	MenuElementList * guiElementList = new MenuElementList(guiBar);
	guiElementList->Add(healthBar);
	guiElementList->Add(grenadeBar);
	//guiElementList->Add(textBox);
	
	Menu * topMenu = new Menu(guiElementList, Vector2D(0,0), GUI_TOP_INDEX);
	guiManager.Add(topMenu);
	CreateConversationBox();
}
void GameManager::TextBoxClicked()
{
	isInConversation = false;
	guiManager.Hide(GUI_CBOX_INDEX);
	camera->ReturnToFocusObject();
}
void GameManager::CreateLevelMenu()
{
	PrintStringList * stringList = new PrintStringList();
	DIR *dp;
 	printf("opening dir\n");
   	
   	float height = 0.9;
   	float size = 0.02;
   	float space = size * 1.5;
   	
   	dp = opendir ("./Levels");
   	printf("dir open\n");
   	if (dp != NULL)
    {
    	struct dirent *ep;
    	while ((ep = readdir (dp)))
    	{
    		
    		if(strcmp(ep->d_name + sizeof(char) * (strlen(ep->d_name)-4), ".lvl") == 0)
    		{
    			printf("Found *%s*\n", ep->d_name);
    			char * string = (char *)malloc(sizeof(char) * (strlen(ep->d_name) + 1));
    			strcpy(string, ep->d_name);
    			PrintString * printString = new PrintString(&(levelManager.textureManager), string,Vector2D(0,0), 0);
        		stringList->Add(printString);
        		height -= space;
    		}
    	}
    	closedir (dp);
    }
    else
		printf ("Couldn't open level directory!");

	MenuSelectionElement * levelList = new MenuSelectionElement("Levels", &(levelManager.textureManager), BUTTON_TEXTURE, Vector2D(-40,0), Vector2D(80,50), NULL, this, &GameManager::SetLoadLevel, stringList);
	Vector2D * textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0,0.25);
	textureVertices[1] = Vector2D(0.5,0.25);
	textureVertices[2] = Vector2D(0.5,0);
	textureVertices[3] = Vector2D(0.0,0.0);
	MenuElement * loadLevelButton = new MenuElement("Load", &(levelManager.textureManager), BUTTON_TEXTURE, Vector2D(-40,55), Vector2D(15,7), textureVertices, this, &GameManager::LoadSelectedLevel);
	textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0,0.25);
	textureVertices[1] = Vector2D(0.5,0.25);
	textureVertices[2] = Vector2D(0.5,0);
	textureVertices[3] = Vector2D(0.0,0.0);
	MenuElement * backButton = new MenuElement("Back", &(levelManager.textureManager), BUTTON_TEXTURE, Vector2D(-20,55), Vector2D(15,7), textureVertices, this, &GameManager::ShowMainMenu);
	MenuElementList * levelSelectionMenuList = new MenuElementList(levelList);
	levelSelectionMenuList->Add(loadLevelButton);
	levelSelectionMenuList->Add(backButton);
	
	Menu * levelMenu = new Menu(levelSelectionMenuList, Vector2D(50,20), LEVEL_MENU_INDEX);
	menuManager.Add(levelMenu);
}
void GameManager::CreateMainMenu()
{
	Vector2D * textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0,0.25);
	textureVertices[1] = Vector2D(0.5,0.25);
	textureVertices[2] = Vector2D(0.5,0);
	textureVertices[3] = Vector2D(0.0,0.0);
	MenuElement * startButton = new MenuElement("Start", &(levelManager.textureManager), BUTTON_TEXTURE, Vector2D(-7.5,0), Vector2D(15,7), textureVertices, this, &GameManager::StartLevel);
	
	textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0,0.25);
	textureVertices[1] = Vector2D(0.5,0.25);
	textureVertices[2] = Vector2D(0.5,0);
	textureVertices[3] = Vector2D(0.0,0.0);
	
	MenuElement * quitButton = new MenuElement("Quit", &(levelManager.textureManager), BUTTON_TEXTURE, Vector2D(-7.5,20), Vector2D(15,7), textureVertices, this, &GameManager::QuitLevel);
	
	textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0,0.25);
	textureVertices[1] = Vector2D(0.5,0.25);
	textureVertices[2] = Vector2D(0.5,0);
	textureVertices[3] = Vector2D(0.0,0.0);
	
	MenuElement * selectLevelButton = new MenuElement("Levels", &(levelManager.textureManager), BUTTON_TEXTURE, Vector2D(-7.5,10), Vector2D(15,7), textureVertices, this, &GameManager::ShowLevelMenu);

	textureVertices = (Vector2D *) malloc(sizeof(Vector2D) * 4);
	textureVertices[0] = Vector2D(0,1);
	textureVertices[1] = Vector2D(1,1);
	textureVertices[2] = Vector2D(1,0);
	textureVertices[3] = Vector2D(0.0,0.0);
	MenuElement * abeFace = new MenuElement( &(levelManager.textureManager), ABE_PORTRAIT_TEXTURE, Vector2D(-17,25), Vector2D(40,55), textureVertices);
	
	
	MenuElementList * mainMenuList = new MenuElementList(startButton);
	mainMenuList->Add(selectLevelButton);
	mainMenuList->Add(quitButton);
	mainMenuList->Add(abeFace);
	
	Menu * mainMenu = new Menu(mainMenuList, Vector2D(50,20), MAIN_MENU_INDEX);
	menuManager.Add(mainMenu);
}

//Button actions
void GameManager::SetLoadLevel(char * level)
{
	levelToLoad = level;
}
void GameManager::LoadSelectedLevel()
{
	printf("Clicked load level button\n");	
	
	if(levelToLoad)
	{
		if(isInLevel)
		{
			Cancel();
		}
		levelManager.LoadLevel(levelToLoad, gameState, &player, &camera, &testBorder, &objectList, &featureList, &stringList, &spriteList, &supplyBag);
		isInLevel = true;
		isInMenu = false;
		isInConversation = false;
		m_pause = false;
		menuManager.Hide(LEVEL_MENU_INDEX);
		guiManager.Show();
	}
}
void GameManager::StartLevel()
{
	printf("Clicked startButton\n");
	menuManager.Hide(MAIN_MENU_INDEX);
	guiManager.Show();
	isInMenu = false;
	m_pause = false;
	if(!isInConversation)
		guiManager.Hide(GUI_CBOX_INDEX);
	if(!isInLevel)
	{
		levelManager.LoadLevel("test.lvl", gameState, &player, &camera, &testBorder, &objectList, &featureList, &stringList,&spriteList, &supplyBag);
		isInLevel = true;
		isInConversation = false;
	}
}
void GameManager::QuitLevel()
{
	printf("Clicked quitButton\n");
	exit = true;
}
void GameManager::ShowLevelMenu()
{
	menuManager.Hide(MAIN_MENU_INDEX);
	menuManager.Show(LEVEL_MENU_INDEX);
	isInMenu = true;
	m_pause = true;	
}
void GameManager::ShowMainMenu()
{
	menuManager.Show(MAIN_MENU_INDEX);
	menuManager.Hide(LEVEL_MENU_INDEX);
	isInMenu = true;
	m_pause = true;	
}
//End button actions
void GameManager::Init(char * directory)
{
	isInLevel = false;
	DIR *dp;
 
 	printf("opening dir\n");
   	
   	float height = 0.9;
   	float size = 0.02;
   	float space = size * 1.5;
   	
   	dp = opendir (directory);
   	printf("dir open\n");
   	if (dp != NULL)
    {
    	struct dirent *ep;
    	while ((ep = readdir (dp)))
    	{
    		
    		if(strcmp(ep->d_name + sizeof(char) * (strlen(ep->d_name)-4), ".lvl") == 0)
    		{
    			printf("Found *%s*\n", ep->d_name);
    			char * str = (char *)malloc(sizeof(char) * (strlen(ep->d_name)+1));
    			strcpy(str, ep->d_name);
        		//printManager.Print(str,Vector2D(0.1,height), size);
        		height -= space;
    		}
    	}
    	closedir (dp);
    }
    else
		printf ("Couldn't open level directory!");
	
	
}
GameManager::~GameManager()
{
	if(objectList)
		delete(objectList);
	objectList = NULL;
	
	if(featureList)
		delete(featureList);
	featureList = NULL;
	
	if(testBorder)
		delete(testBorder);
	testBorder = NULL;
	
	if(stringList)
		delete(stringList);
	stringList = NULL;
	
	player = NULL;
	if(camera)
		delete(camera);
	camera = NULL;
}

void GameManager::Draw()
{

	//printf("drawing\n");
	printManager.Draw();
	menuManager.Draw();
	guiManager.Draw();
	//printf("drawing objects\n");
	if(camera)
			camera->Draw();
	if(isInLevel)
	{
		
		if(spriteList)
			spriteList->Draw();
		if(objectList)
			objectList->Draw();
		if(featureList)
			featureList->Draw();
		if(testBorder)
			testBorder->Draw();
		if(stringList)
			stringList->Draw();
	}
}
void GameManager::Interact(double elapsedTime)
{
	if(isInLevel)
	{
		if(healthBar && player)
			healthBar->SetCurrentValue(player->GetHealth());
		if(objectList)
		{
			objectList->AddGravity();
			player->BeardCollide(objectList);
			objectList->CheckCollisions(elapsedTime);
			if(testBorder)
			{
				objectList->CheckCollisions(elapsedTime, testBorder);
			}
			if(featureList)
			{
				objectList->CheckCollisions(elapsedTime, featureList);
				player->BeardCollide(featureList);
			}
			
			
			/*if(testObject2)
			{
				testObject2->AddGravity();
				testObject->Collision(testObject2);
			}*/
		}
		if(stringList)
		{
			stringList->AddGravity();
		}
		if(spriteList)
			spriteList->CheckCollisions(player, player->supplyBag);
	}
}
void GameManager::Move(float elapsedTime)
{
	if(isInLevel)
	{
		if(objectList)
			objectList->Think(elapsedTime);
		if(stringList)
			stringList->Think(elapsedTime);
		if(spriteList)
			spriteList->Think(elapsedTime);
	}
}

void GameManager::PrepareFrame()
{
	float currentTime = (float)clock()/(float)CLOCKS_PER_SEC;
	float elapsedTime = currentTime - lastThoughtTimestamp;
	//float elapsedTime = difftime(curTime,lastTime);

	if(elapsedTime >= TIME_BETWEEN_POSITIONS)
	{
		lastThoughtTimestamp = currentTime;	
	}
	else
	{
		return;
	}
	if(IsPlaySuspended())
	{
		HandleUserInput();
		if(isInLevel)
			camera->Think(elapsedTime);
		return;
	}
	/*if( elapsedTime <= 0)
	{
		elapsedTime = TIME_BETWEEN_POSITIONS;//5000.0/(float)CLOCKS_PER_SEC;
	}*/
		
	for(;elapsedTime >= TIME_BETWEEN_POSITIONS; elapsedTime -= TIME_BETWEEN_POSITIONS)
	{
		float thisTime = TIME_BETWEEN_POSITIONS;
		if(elapsedTime < TIME_BETWEEN_POSITIONS)
			thisTime = elapsedTime; 
	
		thisTime /= 2.0;
		if(isInLevel)
		{
			Move(thisTime);
			Interact(thisTime);
		}

		HandleUserInput();
		
		if(isInLevel)
		{
			if(objectList)
			{
				//printf("creating\n");
				objectList->CreateObjects();
				//printf("destroying\n");
				objectList->DestroyObjects(player);
			}
			if(featureList)
			{
				//printf("creating2\n");
				featureList->CreateObjects();
				//printf("destroying2\n");
				featureList->DestroyObjects(player);
			}
			camera->Think(thisTime);
		}
		
		
	}
}
